package com.beantools.util;

import com.alibaba.fastjson.JSONObject;
import com.beantools.entity.enums.ConcatTypeEnum;
import com.beantools.entity.EntityStorage;
import com.beantools.filters.BuilderFilters;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Set;

import static com.beantools.entity.enums.ConcatTypeEnum.CONCAT_AND;
import static com.beantools.util.ObjectUtil.isNotNull;
import static com.beantools.util.ObjectUtil.isNull;
import static com.beantools.util.StringUtil.isBlank;

/**
 * 这是数字初步形成签名原串的方法。比如我们在使用第三方接口时，其往往都需要加签和验签。
 * 加签的前提是将对象转化为签名原串。将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>
 * 转化为这种格式：<code>key=WEDNESDAY&value=星期三</code>
 *
 * @author baoya
 * @version 1.0
 * @since 1.1.0
 */

public class SignUtill {

    /**
     * 调用者需要将非格式化或格式化的json字符串按照降序的方式转化为类似于签名原串的形式
     * 比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>转化为这种格式：
     * <code>key=WEDNESDAY&value=星期三</code>
     *
     * @param concat 连接字符串，比如“&”
     * @param json   <code>json</code>字符串
     * @return 类似于签名的字符串
     */
    public static String signJsonSrcToDesc(String concat, String json) {
        return signJsonSrc(false, concat, json);
    }

    /**
     * 调用者需要将非格式化或格式化的json字符串按照升序或降序的方式转化为类似于签名原串的形式
     * 比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>转化为这种格式：
     * <code>key=WEDNESDAY&value=星期三</code>
     *
     * @param concat 连接字符串，比如“&”
     * @param json   <code>json</code>字符串
     * @return 类似于签名的字符串
     */
    public static String signJsonSrcToAsc(String concat, String json) {
        return signJsonSrc(true, concat, json);
    }

    /**
     * 调用者需要将非格式化或格式化的json字符串按照升序或降序的方式转化为类似于签名原串的形式
     * 比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>转化为这种格式：
     * <code>key=WEDNESDAY&value=星期三</code>
     *
     * @param asc    是否按照升序，true表示升序，false表示降序
     * @param concat 连接字符串，比如“&”
     * @param json   <code>json</code>字符串
     * @return 类似于签名的字符串
     */
    public static String signJsonSrc(boolean asc, String concat, String json) {
        if (isBlank(json)) {
            return null;
        }
        if (isNull(concat)) {
            concat = CONCAT_AND.getSymbol();
        }
        Set<String> keyValues = BuilderFilters.builderSignFilters().getKeyValues(json);
        List<String> keyValuesSorted = SortUtil.sort(keyValues, asc);
        String concatStr = ArrayUtil.join(keyValuesSorted, concat);
        return concatStr;
    }

    /**
     * 调用者需要将非格式化或格式化的json字符串按照降序的方式转化为类似于签名原串的形式
     * 比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>转化为这种格式：
     * <code>key=WEDNESDAY&value=星期三</code>
     *
     * @param concat 这是事先定义好的枚举类型
     * @param json   json字符串
     * @return 类似于签名的字符串
     */
    public static String signJsonSrcToDesc(ConcatTypeEnum concat, String json) {
        return signJsonSrc(false, concat, json);
    }

    /**
     * 调用者需要将非格式化或格式化的json字符串按照降序的方式转化为类似于签名原串的形式
     * 比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>转化为这种格式：
     * <code>key=WEDNESDAY&value=星期三</code>
     *
     * @param concat 这是事先定义好的枚举类型
     * @param json   json字符串
     * @return 类似于签名的字符串
     */
    public static String signJsonSrcToAsc(ConcatTypeEnum concat, String json) {
        return signJsonSrc(true, concat, json);
    }

    /**
     * 调用者需要将非格式化或格式化的json字符串按照升序或降序的方式转化为类似于签名原串的形式
     * 比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>转化为这种格式：
     * <code>key=WEDNESDAY&value=星期三</code>
     *
     * @param asc    是否按照升序，true表示升序，false表示降序
     * @param concat 这是事先定义好的枚举类型
     * @param json   json字符串
     * @return 类似于签名的字符串
     */
    public static String signJsonSrc(boolean asc, ConcatTypeEnum concat, String json) {
        if (isNull(concat)) {
            concat = CONCAT_AND;
        }
        return signJsonSrc(asc, concat.getSymbol(), json);
    }

    /**
     * 调用者需要将非格式化或格式化的<code>jsonObject</code>对象按照升序或降序的方式转化为
     * 类似于签名原串的形式。比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}
     * </code>转化为这种格式：<code>key=WEDNESDAY&value=星期三</code>{@link this#signJsonObject(boolean, String, JSONObject)}
     *
     * @param asc        是否按照升序，true表示升序，false表示降序
     * @param concat     这是事先定义好的枚举类型
     * @param jsonObject <code>jsonObject</code>实例化对象
     * @return 类似于签名的字符串
     */
    public static String signJsonObject(boolean asc, String concat, JSONObject jsonObject) {
        if (isNull(jsonObject)) {
            return null;
        }
        return signJsonSrc(asc, concat, jsonObject.toJSONString());
    }

    /**
     * 调用者需要将非格式化或格式化的<code>jsonObject</code>对象按照升序的方式转化为类似于签名原串的形式
     * 比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>转化为这种格式：
     * <code>key=WEDNESDAY&value=星期三</code>
     *
     * @param concat     这是事先定义好的枚举类型
     * @param jsonObject <code>jsonObject</code>实例化对象
     * @return 类似于签名的字符串
     */
    public static String signJsonObjectToAsc(String concat, JSONObject jsonObject) {
        return signJsonObject(true, concat, jsonObject);
    }

    /**
     * 调用者需要将非格式化或格式化的<code>jsonObject</code>对象按照降序的方式转化为类似于签名原串的形式
     * 比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>转化为这种格式：
     * <code>key=WEDNESDAY&value=星期三</code>
     *
     * @param concat     这是事先定义好的枚举类型
     * @param jsonObject <code>jsonObject</code>实例化对象
     * @return 类似于签名的字符串
     */
    public static String signJsonSrcToDesc(String concat, JSONObject jsonObject) {
        return signJsonObject(false, concat, jsonObject);
    }

    /**
     * 调用者需要将非格式化或格式化的实例化的对象按照升序或降序的方式转化为类似于签名原串的形式
     * 比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>转化为这种格式：
     * <code>key=WEDNESDAY&value=星期三</code>
     *
     * @param asc    是否按照升序，true表示升序，false表示降序
     * @param concat 这是事先定义好的枚举类型
     * @param t      实例化的对象
     * @return 类似于签名的字符串
     */
    public static <T> String signObject(boolean asc, ConcatTypeEnum concat, T t) {
        if (isNull(t)) {
            return null;
        }
        Class<T> var1 = (Class<T>) t.getClass();
        Set<EntityStorage> var2 = BuilderFilters.builderMethodFilters().filterByGetter(var1.getMethods());
        if (CollectionUtil.isEmpty(var2)) {
            return null;
        }
        JSONObject jsonObject = new JSONObject();
        for (EntityStorage storage : var2) {
            Object var = null;
            try {
                var = storage.getMethod().invoke(t, null);
                if (isNotNull(var)) {
                    String proName = storage.getPropertyName();
                    jsonObject.put(proName, var);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        return signJsonObject(asc, isNull(concat) ? CONCAT_AND.getSymbol() : concat.getSymbol(), jsonObject);
    }

    /**
     * 调用者需要将非格式化或格式化的实例化的对象按照升序的方式转化为类似于签名原串的形式
     * 比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>转化为这种格式：
     * <code>key=WEDNESDAY&value=星期三</code>
     *
     * @param concat 这是事先定义好的枚举类型
     * @param t      实例化的对象
     * @return 类似于签名的字符串
     */
    public static <T> String signObjectToDesc(ConcatTypeEnum concat, T t) {
        return signObject(false, concat, t);
    }

    /**
     * 调用者需要将非格式化或格式化的例化的对象按照降序的方式转化为类似于签名原串的形式
     * 比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}</code>转化为这种格式：
     * <code>key=WEDNESDAY&value=星期三</code>
     *
     * @return 类似于签名的字符串
     */
    public static <T> String signObjectToAsc(ConcatTypeEnum concat, T t) {
        return signObject(true, concat, t);
    }

    /**
     * 调用者需要将非格式化或格式化的实例化的对象按照调用者指定对象的属性并以升序或降序的方式
     * 转化为类似于签名原串的形式。比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}
     * </code>转化为这种格式：<code>key=WEDNESDAY&value=星期三</code>
     *
     * @param asc    是否按照升序，true表示升序，false表示降序
     * @param concat 这是事先定义好的枚举类型
     * @param concat 当前对象<code>obj</code>指定的数组
     * @param obj    实例化的对象
     * @return 类似于签名的字符串
     */
    public static <T> String signObject(boolean asc, String concat, String[] propNames, Object obj) {
        JSONObject jsonObject = BuilderFilters.builderPropertyFilters().filerJson(propNames, obj);
        if (jsonObject.isEmpty()) {
            return null;
        }
        JSONObject var = new JSONObject();
        for (JSONObject.Entry entry : jsonObject.entrySet()) {
            String proName = (String) entry.getKey();
            Object value = entry.getValue();
            if (isNotNull(value)) {
                var.put(proName, value);
            }
        }
        return signJsonObject(asc, concat, var);
    }

    /**
     * 调用者需要将非格式化或格式化的实例化的对象按照调用者指定对象的属性并以升序的方式
     * 转化为类似于签名原串的形式。比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}
     * </code>转化为这种格式：<code>key=WEDNESDAY&value=星期三</code>
     *
     * @param concat 这是事先定义好的枚举类型
     * @param concat 当前对象<code>obj</code>指定的数组
     * @param obj    实例化的对象
     * @return 类似于签名的字符串
     */
    public static <T> String signJsonSrcToAsc(String concat, String[] propNames, Object obj) {
        return signObject(true, concat, propNames, obj);
    }

    /**
     * 调用者需要将非格式化或格式化的实例化的对象按照调用者指定对象的属性并以降序的方式
     * 转化为类似于签名原串的形式。比如，将这种字符串<code>{ "key": "WEDNESDAY",value": "星期三"}
     * </code>转化为这种格式：<code>key=WEDNESDAY&value=星期三</code>
     *
     * @param concat 这是事先定义好的枚举类型
     * @param concat 当前对象<code>obj</code>指定的数组
     * @param obj    实例化的对象
     * @return 类似于签名的字符串
     */
    public static <T> String signObjectToDesc(String concat, String[] propNames, Object obj) {
        return signObject(false, concat, propNames, obj);
    }
}
